﻿/* 
 * Enterprise Library Extensions - DI + ORM.
 * Copyright (C) 2006
 *
 * GNU General Public License
 *
 * This program is free software; you can redistribute 
 * it and/or modify it under the terms of the GNU General Public License 
 * as published by the Free Software Foundation; either version 2 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 * See the GNU General Public License for more details.
 */
#region Using directives

using System;
using System.Text;
using System.Collections;
using System.IO;
using System.Xml;
using ELE.Common.Util;

#endregion

namespace ELE.EnterpriseLibrary.Naming.Configuration
{
  /// <summary>
  /// 
  /// </summary>
	public class GenericConfigurationParser
  {
    /// <summary>
    /// 
    /// </summary>
    /// <param name="pNode"></param>
    /// <returns></returns>
    protected static object GetComponentNodeValue(XmlNode pNode)
    {
      return GetNodeValue(pNode, true);
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="pNode"></param>
    /// <returns></returns>
    protected static object GetNodeValue(XmlNode pNode)
    {
      return GetNodeValue(pNode, false);
    }
    /// <summary>
    /// Extract a value from a node. We have the following types:
    /// <nl>
    ///   <li>string values</li>
    ///   <li>EnvironmentClassName</li>
    ///   <li>a list</li>
    ///   <li>a hashmap</li>
    /// </nl> 
    /// </summary>
    /// <param name="pNode">The node</param>
    /// <param name="componentNode">whether it is a component node or not, 
    /// this eliminates the search for class definitions if true, or references if false</param>
    /// <returns></returns>
    protected static object GetNodeValue(XmlNode pNode, bool componentNode)
    {
      if (pNode == null || !pNode.HasChildNodes)
      {
        return null;
      }
      if (pNode.FirstChild.NodeType.Equals(XmlNodeType.Text))
      {
        return pNode.InnerText;
      }
      else if (pNode.HasChildNodes)
      {
        XmlNode listNode = SelectNodeByNameIgnoreCase(pNode, GenericConfiguration.TAGNAME_LIST);
        if (listNode != null)
        {
          IList items = new ArrayList();
          IList itemNodes = SelectNodesByNameIgnoreCase(listNode, GenericConfiguration.TAGNAME_ITEM);
          if (itemNodes == null)
          {
            return null;
          }
          if (listNode.Attributes[ComponentConfiguration.TAGNAME_TYPE] == null &&
             listNode.Attributes[ComponentConfiguration.ATTRIBUTENAME_GENERICTYPE] == null)
          {
            foreach (XmlNode iNode in itemNodes)
            {
              items.Add(GetNodeValue(iNode, componentNode));
            }
            return items;
          }
          else
          {
            GenericCollectionInitializer gci = new GenericCollectionInitializer();
            gci.Type = GenericCollectionInitializer.TYPE_LIST;
            if (listNode.Attributes[ComponentConfiguration.TAGNAME_TYPE] != null)
            {
              gci.GenericType = listNode.Attributes[ComponentConfiguration.TAGNAME_TYPE].Value;
            }
            if (listNode.Attributes[ComponentConfiguration.ATTRIBUTENAME_GENERICTYPE] != null)
            {
              gci.GenericValueType = listNode.Attributes[ComponentConfiguration.ATTRIBUTENAME_GENERICTYPE].Value;
            }
            foreach (XmlNode iNode in itemNodes)
            {
              gci.AddEntry(GetNodeValue(iNode, componentNode));
            }
            return gci;
          }
        }
        else
        {
          XmlNode hashNode = SelectNodeByNameIgnoreCase(pNode, GenericConfiguration.TAGNAME_HASHMAP);
          XmlNode dictNode = SelectNodeByNameIgnoreCase(pNode, GenericConfiguration.TAGNAME_DICTIONARY);
          if (hashNode != null)
          {
            Hashtable map = new Hashtable();
            foreach (XmlNode mapNode in hashNode.ChildNodes)
            {
              if (mapNode.Name == GenericConfiguration.TAGNAME_ADD)
              {
                map.Add(mapNode.Attributes[GenericConfiguration.ATTRIBUTENAME_NAME].Value, GetNodeValue(mapNode, componentNode));
              }
            }
            return map;
          }
          else if (dictNode != null)
          {
            GenericCollectionInitializer gci = new GenericCollectionInitializer();
            gci.Type = GenericCollectionInitializer.TYPE_DICTIONARY;
            if (dictNode.Attributes[ComponentConfiguration.TAGNAME_TYPE] != null)
            {
              gci.GenericType = dictNode.Attributes[ComponentConfiguration.TAGNAME_TYPE].Value;
            }
            else
            {
              gci.GenericType = GenericCollectionInitializer.GENERICTYPE_DICTIONARY;
            }
            if (dictNode.Attributes[ComponentConfiguration.ATTRIBUTENAME_GENERICKEYTYPE] != null)
            {
              gci.GenericKeyType = dictNode.Attributes[ComponentConfiguration.ATTRIBUTENAME_GENERICKEYTYPE].Value;
            }
            if (dictNode.Attributes[ComponentConfiguration.ATTRIBUTENAME_GENERICVALUETYPE] != null)
            {
              gci.GenericValueType = dictNode.Attributes[ComponentConfiguration.ATTRIBUTENAME_GENERICVALUETYPE].Value;
            }
            foreach (XmlNode addNode in dictNode.ChildNodes)
            {
              if (addNode.Name == GenericConfiguration.TAGNAME_ADD)
              {
                //TODO : Think about throwing exceptions here if not properly configured however schema validation  should have caught these errors
                XmlNode keyNode = SelectNodeByNameIgnoreCase(addNode, ComponentConfiguration.TAGNAME_KEY);
                if (keyNode != null)
                {
                  XmlNode valueNode = SelectNodeByNameIgnoreCase(addNode, ComponentConfiguration.TAGNAME_VALUE);
                  if (valueNode != null)
                  {
                    KeyValuePair kvp = new KeyValuePair();
                    kvp.Key = GetNodeValue(keyNode, componentNode);
                    kvp.Value = GetNodeValue(valueNode, componentNode);
                    gci.AddEntry(kvp);
                  }
                }
              }
            }
            return gci;
          }
          else
          {
            if (componentNode)
            {
              XmlNode refNode = SelectNodeByNameIgnoreCase(pNode, GenericConfiguration.TAGNAME_REFERENCE);
              if (refNode != null)
              {
                ComponentInitializer cInit = new ComponentInitializer();
                cInit.Type = ComponentInitializer.TYPE_REF;
                cInit.Value = GetNodeValue(refNode);
                return cInit;
              }
            }
            else
            {
              XmlNode classNode = SelectNodeByNameIgnoreCase(pNode, GenericConfiguration.TAGNAME_CLASS);
              if (classNode != null)
              {
                XmlNode assemblyNode = SelectNodeByNameIgnoreCase(pNode, GenericConfiguration.TAGNAME_ASSEMBLY);
                ClassAssemblyPair val = new ClassAssemblyPair(classNode.InnerText);
                val.ClassName = classNode.InnerText;
                if (assemblyNode != null)
                {
                  val.AssemblyName = assemblyNode.InnerText;
                }
                return val;
              }
            }
          }
        }
      }
      return null;
    }
    /// <summary>
    /// Extract a node by name ignoring case. First try is the name as given, then all uppercase, then capitalized
    /// </summary>
    /// <param name="pParentNode">The parent node</param>
    /// <param name="pNodeName">the name</param>
    /// <returns>The first node found in the parent with the given name ignoring case</returns>
    protected static XmlNode SelectNodeByNameIgnoreCase(XmlNode pParentNode, string pNodeName)
    {
      XmlNode node = null;
      node = pParentNode.SelectSingleNode(GenericConfiguration.CHILD_KEYWORD + pNodeName);
      if (node == null)
      {
        node = pParentNode.SelectSingleNode(GenericConfiguration.CHILD_KEYWORD + pNodeName.ToLower());
      }
      if (node == null)
      {
        node = pParentNode.SelectSingleNode(GenericConfiguration.CHILD_KEYWORD + pNodeName.ToUpper());
      }
      if (node == null)
      {
        node = pParentNode.SelectSingleNode(GenericConfiguration.CHILD_KEYWORD + StringUtils.Capitalize(pNodeName));
      }
      return node;
    }
    /// <summary>
    /// Extract a list of nodes by name ignoring case.
    /// First try is the name as given, then all uppercase, then capitalized
    /// </summary>
    /// <param name="pParentNode">The parent node</param>
    /// <param name="pNodeName">the name</param>
    /// <returns>The list of nodes found in the parent with the given name ignoring case</returns>
    protected static IList SelectNodesByNameIgnoreCase(XmlNode pParentNode, string pNodeName)
    {
      IList nodes = new ArrayList();
      XmlNodeList nodeList = pParentNode.SelectNodes(GenericConfiguration.CHILD_KEYWORD + pNodeName);
      
      if(!pNodeName.Equals(pNodeName.ToLower()))
      {
        XmlNodeList nodeListLower = pParentNode.SelectNodes(GenericConfiguration.CHILD_KEYWORD + pNodeName.ToLower());
        if (nodeListLower != null)
        {
          foreach (XmlNode node in nodeListLower)
          {
            nodes.Add(node);
          }
        }
      }
      if (!pNodeName.Equals(pNodeName.ToUpper()))
      {
        XmlNodeList nodeListUpper = pParentNode.SelectNodes(GenericConfiguration.CHILD_KEYWORD + pNodeName.ToUpper());
        if (nodeListUpper != null)
        {
          foreach (XmlNode node in nodeListUpper)
          {
            nodes.Add(node);
          }
        }
      }
      if (!pNodeName.Equals(StringUtils.Capitalize(pNodeName)))
      {
        XmlNodeList nodeListCaps = pParentNode.SelectNodes(GenericConfiguration.CHILD_KEYWORD + StringUtils.Capitalize(pNodeName));
        if (nodeListCaps != null)
        {
          foreach (XmlNode node in nodeListCaps)
          {
            nodes.Add(node);
          }
        }
      }
      if (nodeList != null)
      {
        foreach (XmlNode node in nodeList)
        {
          nodes.Add(node);
        }
      }
      return nodes;
    }
    /// <summary>
    /// From an xml node return a defined type :
    /// <code>
    ///   &lt;type name="Namespace.ClassName" assembly="AssemblyName"/>
    /// </code>
    /// </summary>
    /// <param name="pNode"></param>
    /// <returns></returns>
    public static ClassAssemblyPair GetClassAssemblyPairFromNode(XmlNode pNode)
    {
      ClassAssemblyPair pair = null;
      if (pNode.Attributes[GenericConfiguration.TAGNAME_TYPE] != null)
      {
        pair = new ClassAssemblyPair(pNode.Attributes[GenericConfiguration.TAGNAME_TYPE].Value, null);
      }
      else if (pNode.Attributes[GenericConfiguration.ATTRIBUTENAME_NAME] != null)
      {
        pair = new ClassAssemblyPair(pNode.Attributes[GenericConfiguration.ATTRIBUTENAME_NAME].Value, null);
      }
      if(pair != null)
      {
        if (pNode.Attributes[GenericConfiguration.TAGNAME_ASSEMBLY] != null)
        {
          pair.AssemblyName = pNode.Attributes[GenericConfiguration.TAGNAME_ASSEMBLY].Value;
        }
      }
      return pair;
    }
  }
}
